"""
oyHierarchyInstancer by Erkan Ozgur Yilmaz (c) 2010

v10.6.17

Description:
------------
Given a group, it instances the hirerary. It is written to avoid having
instanced groups in the scene. Thus multi instancing is avoided.

Version History:
----------------
v10.6.17
- the script now works with single object hierarchies
- after the script finishes it job, it selects the created top most node

v10.6.12
- initial working version
"""



import pymel.core as pm



__version__ = '10.6.12'




########################################################################
class HierarchyInstancer(object):
    """the hierarchy object
    """
    
    
    
    #----------------------------------------------------------------------
    def __init__(self):
        self._instancables = []
        
        self.addInstancable( pm.nodetypes.Mesh )
        self.addInstancable( pm.nodetypes.NurbsCurve )
        self.addInstancable( pm.nodetypes.NurbsSurface )
        self.addInstancable( pm.nodetypes.Subdiv )
        self.addInstancable( pm.nodetypes.Locator )
    
    
    
    #----------------------------------------------------------------------
    def addInstancable(self, objType):
        """adds new instancable object type, the type should be a
        pymel.core.nodeType class
        """
        self._instancables.append( objType )
    
    
    
    #----------------------------------------------------------------------
    def walkHierarchy(self, dagNode):
        """for the given dag node, walks through the hierarchy
        """
        
        assert( isinstance(dagNode, pm.nodetypes.Transform) )
        
        nodes = []
        
        for node in dagNode.getChildren():
            
            # try to get children if it is a transform node
            if isinstance(node, pm.nodetypes.Transform):
                childNodes = self.walkHierarchy( node )
                
                nodes.append( node )
                nodes += childNodes
        
        return nodes
    
    
    
    #----------------------------------------------------------------------
    def instance(self, sourceTransformNode):
        """instances the given nodes hierarchy
        """
        
        # duplicate the given node
        # then replace the instancable nodes with instances
        
        # find instancable nodes in the node and dupNode
        sourceHierarchy = self.walkHierarchy( sourceTransformNode )
        
        # if there is no node in the sourceHierarchy just return
        # the instance of the given node
        if len(sourceHierarchy) < 1:
            dupNode = pm.duplicate( sourceTransformNode, ilf=1, rc=True )[0]
            pm.select( dupNode )
            return
        
        
        dupNode = pm.duplicate( sourceTransformNode, rc=True )[0]
        dupHierarchy = self.walkHierarchy( dupNode )
        
        for i, node in enumerate(dupHierarchy):
            
            shape = node.getShape()
            if shape is not None and isinstance(shape, tuple(self._instancables) ):
                # instance the corresponding sourceNode
                sourceNode = sourceHierarchy[i]
                newInstanceNode = pm.duplicate( sourceNode, ilf=True )[0]
                
                pm.parent( newInstanceNode, node.getParent(), r=False )
                pm.delete( node )
        
        pm.select( dupNode )